/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package edu.asu.cse360.service.impl;

import edu.asu.cse360.annotation.TargetEntity;
import edu.asu.cse360.dao.DataAccessObject;
import edu.asu.cse360.model.Chef;
import edu.asu.cse360.model.CustomerSession;
import edu.asu.cse360.model.Employee;
import edu.asu.cse360.model.ItemOrder;
import edu.asu.cse360.model.Manager;
import edu.asu.cse360.model.MenuItem;
import edu.asu.cse360.model.OrderStatus;
import edu.asu.cse360.model.Restaurant;
import edu.asu.cse360.model.WaitPerson;
import edu.asu.cse360.service.ItemOrderService;
import edu.asu.cse360.service.OrderStatusService;
import edu.asu.cse360.util.EmployeeVisitor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("itemOrderService")
public class ItemOrderServiceImpl implements ItemOrderService{

    private EmployeeVisitor getCurrentOrdersVisitor(){
        return new EmployeeVisitor(){
            @Override
            public Object visit(Manager manager) {
              return getCurrentOrders(manager);
            }

            @Override
            public Object visit(WaitPerson waitPerson) {
              return getCurrentOrders(waitPerson);
            }

            @Override
            public Object visit(Chef chef) {
              return getCurrentOrders(chef);
            }
        };
    }

    @Autowired
    @TargetEntity(ItemOrder.class)
    private DataAccessObject<ItemOrder, Integer> itemOrderDao;

    @Autowired
    private OrderStatusService orderStatusSvc;

    @Override
    public List<ItemOrder> getCurrentOrders(Employee employee) {
        return (List<ItemOrder>) employee.accept(getCurrentOrdersVisitor());
    }
    

    private List<ItemOrder> getCurrentOrders(Manager manager){
        Restaurant restaurant = manager.getRestaurant();
        return getOrdersMatchingStatuses(new OrderStatus[]{OrderStatus.COOKING, OrderStatus.DONE_COOKING, OrderStatus.NOT_STARTED}, restaurant);
    }

    private List<ItemOrder> getCurrentOrders(WaitPerson waitPerson){
        List<OrderStatus> allowedStatuses = orderStatusSvc.getStatusViewableByWaitPerson();
        return getOrdersMatchingStatuses(allowedStatuses, waitPerson.getRestaurant());
    }

    private List<ItemOrder> getCurrentOrders(Chef chef){
        List<OrderStatus> allowedStatuses = orderStatusSvc.getStatusViewableByChef();
        return getOrdersMatchingStatuses(allowedStatuses, chef.getRestaurant());
    }


    private List<ItemOrder> getOrdersMatchingStatuses(OrderStatus[] orderStatuses, Restaurant restaurant) {
        return getOrdersMatchingStatuses(Arrays.asList(orderStatuses), restaurant);
    }

    private List<ItemOrder> getOrdersMatchingStatuses(Collection<OrderStatus> statuses, Restaurant restaurant) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("statuses", statuses);
        params.put("restaurant", restaurant);
        return itemOrderDao.findList(ItemOrder.FIND_BY_STATUS_INCLUSIVE, params);
    }

    @Override
    public ItemOrder setStatus(ItemOrder itemOrder, OrderStatus newStatus, Employee employee) {
        if((newStatus == OrderStatus.COOKING || newStatus == OrderStatus.DONE_COOKING) && employee instanceof Chef){
            itemOrder.setChef((Chef) employee);
        }else if(newStatus == OrderStatus.DELIVERED){
            itemOrder.setDeliverTime(new DateTime());
            if(employee instanceof WaitPerson){
                itemOrder.setWaitPerson((WaitPerson)employee);
            }else{
                throwInvalidEmployeeForSetStatus(newStatus, "WaitPerson");
            }
        }
        itemOrder.setStatus(newStatus);
        return this.save(itemOrder);
    }

    @Override
    public ItemOrder save(ItemOrder itemOrder) {
        if(itemOrder.getId() == null){
            itemOrderDao.create(itemOrder);
        }else{
            itemOrderDao.update(itemOrder);
        }
        return itemOrder;
    }

    @Override
    public List<ItemOrder> getItemOrders(Restaurant restaurant, DateTime start, DateTime end) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("restaurant", restaurant);
        params.put("startDate", start.toCalendar(Locale.US));
        params.put("endDate", end.toCalendar(Locale.US));
        return itemOrderDao.findList(ItemOrder.FIND_BY_DATE_RANGE, params);
    }

    @Override
    public ItemOrder find(Integer id) {
        return itemOrderDao.read(id);
    }

    private void throwInvalidEmployeeForSetStatus(OrderStatus newStatus, String employeeType) {
        throw new IllegalArgumentException("If the new status for the order is '"+newStatus+"', then the corresponding employee should be a"+employeeType);
    }

    @Override
    public List<ItemOrder> getItemOrders(CustomerSession customerSession) {
        if(customerSession == null || customerSession.getId() == null) return new ArrayList<ItemOrder>();
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("customerSession", customerSession);
        return itemOrderDao.findList(ItemOrder.FIND_BY_SESSION, params);
    }

    @Override
    public void deleteAllTiedToMenuItem(MenuItem menuItem) {
       List<ItemOrder> itemOrders = findByMenuItem(menuItem);
       for(ItemOrder itemOrder : itemOrders){
            this.remove(itemOrder);
       }
    }

    private List<ItemOrder> findByMenuItem(MenuItem menuItem) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("menuItem", menuItem);
        return itemOrderDao.findList(ItemOrder.FIND_BY_MENU_ITEM, params);
    }

    @Override
    public ItemOrder remove(ItemOrder itemOrder) {
        itemOrderDao.delete(itemOrder);
        return itemOrder;
    }

    @Override
    public List<ItemOrder> findByWaitPerson(WaitPerson waitPerson) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("waitPerson", waitPerson);
        return itemOrderDao.findList(ItemOrder.FIND_BY_WAIT_PERSON, params);
    }

    @Override
    public List<ItemOrder> findByChef(Chef chef) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("chef", chef);
        return itemOrderDao.findList(ItemOrder.FIND_BY_CHEF, params);
    }




}
